<?php
/** JavaScript-type array class, or Perl-type list
*   Index may only be a number, and indices are contiguous (?)
*   @todo ensure that indices are contiguous:
*   - disallow set() for non-existent indices
*   - reevaluate indices after unset()
*/

class Yz_Array
    extends Yz_Array_Abstract
    implements Iterator,ArrayAccess,Countable,Yz_Array_Interface
{


    /******************************
    ***     Public methods      ***
    *******************************/

    /**
     *  get the list's value as php native array[index:value]
     *  @param mixed $value
     *  @return null|array the array's values with numeric contiguous indices, or null if value is not an array-type
     */
    public static function valueOf( $value )
    {
        $val = parent::valueOf( $value );
        if ( $value instanceof Yz_Array )
            // all OK, we've already gotten the right values
            true;
        else if (null !== $val)
            // ensure contiguous numeric indices
            $val = array_values($val);

        return $val;
    } // valueOf


    /** push argument into the array (insert it after all other elements)
     *  @param mixed $arg
     *  @return chainable
     */
    public function push( $args )
    {
        $this->__dict__[] = $args;
        return $this;
    } // push

    /** pop an element from the array (take the last element and remove it from the array)
     *  @return mixed
     */
    public function pop()
    {
        return array_pop($this->__dict__);
    } // pop


    /** shift an element from the array (take the first element and remove it from the array)
     *  @return mixed
     */
    public function shift()
    {
        return array_shift($this->__dict__);
    } // push

    /** unshift argument into the array (insert it before all other elements)
     *  @param mixed $arg
     *  @return chainable
     */
    public function unshift( $args )
    {
        debug();

        array_unshift($this->__dict__, $args);
        return $this;
    } // pop


    /**
     *  filter elements using a callback and reindex the remaining elements
     *  @param callback
     *  @return chainable
     */
    public function filter( $callback=null )
    {
        parent::filter($callback);
        $this->__dict__ = array_values($this->__dict__);
        return $this;
    }

    /** append elements of the input array to the end of this array
     *  (same as push()'ing each element)
     *  @param Yz_Array_Interface $array
     *  @return chainable
     */
    public function append( Yz_Array_Interface $array )
    {
        $append = $this::valueOf($array);
        $this->__dict__ = array_merge($this->__dict__, $append);
        return $this;
    }


    /**
    *   @param int $index if < 0, then counting the offset from the end of list
    *   @return int
    *   @throw InvalidArgument if index not valid
    *   @throw IndexOutOfBounds if index not found
    */
    protected function adjustIndex( $index )
    {
        $index = Yz_Int::valueOf( $index );
        if (null === $index)
            throw TypeError::_new_();
        $count = count($this->__dict__);
        if ( $index < 0 ) {
            if ($index < -$count) // NOTE: array[-$count] is equal to array[0]
                throw IndexError::_new_(array('index' => $index));
            $index += $count;
        } else if ( $index >= $count )
            throw IndexError::_new_(array('index' => $index));

        return $index;
    } // adjustIndex

    /** test if the index exists in Array, called after index verification
     *  @param string index (after adjustIndex)
     *  @return bool
     */
    protected function _has_( $index )
    {
        return true;
    }

    /** the default unsetter is called after index verification
     *  @param string index
     *  @return chainable
     */
    protected function _del_( $index )
    {
        parent::_del_( $index );
        $this->__dict__ = array_values($this->__dict__);
        return $this;
    }

} // class

